package com.fulihui.usercore.core.processor.auth;

import com.fulihui.usercore.common.util.Errors;
import com.fulihui.usercore.common.util.VersionFormat;
import com.fulihui.usercore.config.RedisUtil;
import com.fulihui.usercore.core.generator.IGenerator;
import com.fulihui.usercore.core.processor.vcode.VcodeProcessor;
import com.fulihui.usercore.core.processor.vcode.VcodeProcessorRegistor;
import com.fulihui.usercore.core.repository.CustomerRepositoery;
import com.fulihui.usercore.core.repository.UserLoginRepository;
import com.fulihui.usercore.core.repository.UserRepository;
import com.fulihui.usercore.dal.dataobj.LoginDO;
import com.fulihui.usercore.dto.CustomerDTO;
import com.fulihui.usercore.dto.DateConv;
import com.fulihui.usercore.dto.UserDTO;
import com.fulihui.usercore.enums.UserGenderEnum;
import com.fulihui.usercore.enums.UserStateEnum;
import com.fulihui.usercore.enums.VcodeProcessTypeEnum;
import com.fulihui.usercore.request.auth.AbstractAuthRequest;
import com.fulihui.usercore.request.auth.WechatAuthRequest;
import com.fulihui.usercore.request.vcode.VcodeCheckRequest;
import com.fulihui.usercore.sdk.domain.SignTypeEnum;
import com.fulihui.usercore.sdk.domain.UserLoginRegisterMessage;
import org.near.servicesupport.error.CommonServiceErrorEnum;
import org.near.servicesupport.error.InvokeServiceException;
import org.near.servicesupport.request.RequestBuilder;
import org.near.toolkit.common.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;



/**
 * @author Willard.Hu on 2016/11/9 0009.
 */
public abstract class AbstractAuthProcessor implements AuthProcessor {
    protected final transient Logger log = LoggerFactory.getLogger(getClass());

    static ExecutorService executor = Executors.newFixedThreadPool(16);

    @Autowired
    UserRepository userRepository;
    @Autowired
    UserLoginRepository userLoginRepository;
    @Autowired
    CustomerRepositoery customerRepositoery;
    @Autowired
    TransactionTemplate transactionTemplate;
    @Autowired
    IGenerator nicknameGenerator;
    @Autowired
    VcodeProcessorRegistor vcodeProcessorRegistor;
    @Autowired
    RedisUtil redisUtil;


    /**
     * 公共注册处理
     *
     * @see AuthProcessor#register(AbstractAuthRequest)
     */
    @Override
    public String register(AbstractAuthRequest request) {
        registerCheck(request);

        TransactionCallback<String> action = (status -> {
            LoginDO registered = userLoginRepository.queryByPK(request.getLoginId());
            // 未注册的执行插入数据，现在微信会有后绑定情况，所以不能重复插入数据
            // 只做绑定，存在账号不能注册在 registerCheck 中处理
            String userId;
            if (registered == null) {
                UserDTO userDTO = toUserModel(request);
                userId = userRepository.create(userDTO);
                LoginDO loginDO = toLoginModel(userId, request);
                userLoginRepository.insert(loginDO);
            } else {
                userId = registered.getUserId();
            }
            registerExtensions(userId, request);
            return userId;
        });
        String userId = transactionTemplate.execute(action);

        // 统一注册方法，子类重写此方法，需要加上这段逻辑,   用户注册、登入发布消息
        signPublish(request, userId, SignTypeEnum.SIGN_UP);
        // 此处认为注册就是首次登入，历史用户不处理

        return userId;
    }

    /**
     * 用户注册、登入发布消息
     *
     * @param request {@link AbstractAuthRequest}
     */
    void signPublish(AbstractAuthRequest request, String userId, SignTypeEnum signType) {
        UserLoginRegisterMessage userRegisterMessage = new UserLoginRegisterMessage();
        userRegisterMessage.setLoginId(request.getLoginId());
        userRegisterMessage.setUserId(userId);
        userRegisterMessage.setSignType(signType.getCode());
        userRegisterMessage.setSignSourceCode(request.getRegisterSource());
        userRegisterMessage.setMechineNo(request.getMachineNo());

        log.info("开始推送注册登入消息...userId={}", userId);
        int maxReties = 3, retryTiems = 0;
        long rs = 0;

        log.info("推送注册登入消息成功,userId={}", userId);

    }

    /**
     * 注册验证
     *
     * @param request {@link AbstractAuthRequest} 的子类
     */
    void registerCheck(AbstractAuthRequest request) {
        if (registered(request.getLoginId())) {
            throw new InvokeServiceException(Errors.User.USER_HAS_REGISTERED);
        }
    }

    /**
     * 公共验证账号是否已注册，定义为final，不让子类再重写扩展
     *
     * @param loginId 用户登陆账号
     * @return 是否已注册 true/false
     */
    @Override
    public final boolean registered(String loginId) {
        return userLoginRepository.queryByPK(loginId) != null;
    }

    /**
     * 授权类型
     *
     * @return {@link com.fulihui.usercore.enums.UserRegisterTypeEnum}
     */
    public abstract String authType();

    /**
     * 通过请求参数生成用户模型
     *
     * @param request {@link AbstractAuthRequest} 的子类
     * @return {@link UserDTO}
     */
    abstract UserDTO toUserModel(AbstractAuthRequest request);

    /**
     * 通过请求参数生成登陆账号模型
     *
     * @param request {@link AbstractAuthRequest} 的子类
     * @return {@link LoginDO}
     */
    protected LoginDO toLoginModel(String userId, AbstractAuthRequest request) {
        LoginDO loginDO = new LoginDO();
        loginDO.setLoginId(request.getLoginId());
        loginDO.setUserId(userId);
        return loginDO;
    }

    /**
     * 对注册的扩展，默认什么都不做，子类需要扩展注册方法，重写此方法
     *
     * @param request {@link AbstractAuthRequest} 的子类
     */
    void registerExtensions(String userId, AbstractAuthRequest request) {
        // Nothing to do ...
    }

    /**
     * 检查登入时用户状态
     *
     * @param userId 用户唯一标识
     */
    void checkLoginUserState(String userId) {
        UserDTO userDTO = userRepository.queryByPK(userId);
        if (UserStateEnum.FORBID.getCode().equals(userDTO.getState())) {
            throw new InvokeServiceException(Errors.User.FORBID_USER_STATE);
        }
    }

    /**
     * 通过手机号创建客户，目前只有手机号是客户的身份识别方式
     *
     * @param mobileNo 手机号
     * @param userId   用户唯一标识，更新时传入判断是否有客户信息，没有的话，就补绑定
     * @return 客户唯一标识 customerId
     */
    String bindingCustomer(String mobileNo, String userId) {
        Assert.hasLength(mobileNo);

        TransactionCallback<String> action = (status -> {
            CustomerDTO condition = new CustomerDTO();
            condition.setMobileNo(mobileNo);
            List<CustomerDTO> rsList = customerRepositoery.query(condition);
            String customerId;
            if (CollectionUtils.isEmpty(rsList)) {
                CustomerDTO customerDTO = new CustomerDTO();
                customerDTO.setMobileNo(mobileNo);
                customerId = customerRepositoery.create(customerDTO);
                // 历史用户没有客户信息，更新用户customerId
                if (StringUtil.isNotBlank(userId)) {
                    UserDTO userDTO = new UserDTO();
                    userDTO.setUserId(userId);
                    userDTO.setCustomerId(customerId);
                    boolean b = userRepository.modify(userDTO);
                    if (!b) {
                        throw new InvokeServiceException(CommonServiceErrorEnum.SYSTEM_ERROR);
                    }
                }
            } else {
                customerId = rsList.get(0).getCustomerId();
                // 客户有其他用户账号的情况，目前不会发生，手机登录号就一个，未以后非手机号账号的情况做处理
                if (StringUtil.isNotBlank(userId)) {
                    UserDTO userDTO = userRepository.queryByPK(userId);
                    if (StringUtil.isBlank(userDTO.getCustomerId())) {
                        UserDTO update = new UserDTO();
                        update.setUserId(userId);
                        update.setCustomerId(customerId);
                        boolean b = userRepository.modify(update);
                        if (!b) {
                            throw new InvokeServiceException(CommonServiceErrorEnum.SYSTEM_ERROR);
                        }
                    }
                }
            }
            return customerId;
        });

        return transactionTemplate.execute(action);
    }

    /**
     * 验证短息验证码
     *
     * @param loginId   登陆账号
     * @param smsCode   短信验证码
     * @param namespace 命名空间
     * @return 验证结果 true/false
     */
    boolean checkSmsCode(String loginId, String smsCode, String namespace) {
        VcodeCheckRequest vcodeCheckRequest = RequestBuilder.build(VcodeCheckRequest.class);
        vcodeCheckRequest.setAccountNo(loginId);
        vcodeCheckRequest.setVcode(smsCode);
        vcodeCheckRequest.setVcodeNamespace(namespace);
        VcodeProcessor processor = vcodeProcessorRegistor.getProcessor(VcodeProcessTypeEnum.MOBILE_SMS);
        return processor.check(vcodeCheckRequest);
    }

    /**
     * 异步处理，用于处理用户登入（不含注册）后的使用产品和最后登入时间记录
     */
    void asyncSignInExecute(String userId, AbstractAuthRequest request) {
        executor.execute(() -> {
            try {
                if (request == null) {
                    throw new IllegalArgumentException("AbstractAuthRequest can not be null.");
                }
                if (StringUtil.isBlank(userId)) {
                    throw new IllegalArgumentException("Userid can not be blank.");
                }
                UserDTO old = userRepository.queryByPK(userId);
                if (old == null) {
                    throw new IllegalArgumentException("Not exists user:" + userId);
                }
                UserDTO record = new UserDTO();
                WechatAuthRequest wechatAuthRequest = (WechatAuthRequest) request;
                if (wechatAuthRequest.getSex() == 1) {
                    record.setGender(UserGenderEnum.MEN.getCode());
                } else if (wechatAuthRequest.getSex() == 2) {
                    record.setGender(UserGenderEnum.WOMEN.getCode());
                } else {
                    record.setGender(UserGenderEnum.UNKNOW.getCode());
                }
                record.setUserId(userId);
                if (request.getUsedProduct() != null) { // 使用产品入参不为空时，更新数据库字段
                    List<String> newUsedProducts = new ArrayList<>();
                    // 将旧的使用产品信息加入到新的做全量更新
                    if (old.getUsedProducts() != null) {
                        newUsedProducts.addAll(old.getUsedProducts());
                    }
                    // 历史使用产品记录不包含入参的使用产品才添加
                    if (record.getUsedProducts() == null
                            || !record.getUsedProducts().contains(request.getUsedProduct())) {
                        newUsedProducts.add(request.getUsedProduct());
                    }
                    record.setUsedProducts(newUsedProducts);
                }
                record.setLastLogin(DateConv.d2s(new Date())); // 记录最后登入时间
                record.setErduoVersion(request.getErduoVersion());
                // 版本有所提升
                if (request.getErduoVersion() != null &&
                        VersionFormat.atoi(old.getErduoVersion()) < VersionFormat.atoi(request.getErduoVersion())) {
                }
                boolean b = userRepository.modify(record);
                if (!b) {
                    throw new RuntimeException("Failed to update user usedProduct.");
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        });
    }


}
